From ff3b8e6581269601cec1b4675a3be47135db778b Mon Sep 17 00:00:00 2001 From: robertl Date: Mon, 19 Jun 2006 14:42:20 +0000 Subject: [PATCH] Andy Armstrong contributes support for Wintec WBT-200. --- gpsbabel/Makefile.in | 3 +- gpsbabel/msvc/GPSBabel.dsp | 4 + gpsbabel/msvc/GPSBabel.vcproj | 3 + gpsbabel/msvc/build.bat | 2 +- gpsbabel/vecs.c | 7 + gpsbabel/wbt-200.c | 337 ++++++++++++++++++ gpsbabel/xmldoc/formats/options/wbt-erase.xml | 1 + gpsbabel/xmldoc/formats/wbt.xml | 9 + 8 files changed, 364 insertions(+), 2 deletions(-) create mode 100644 gpsbabel/wbt-200.c create mode 100644 gpsbabel/xmldoc/formats/options/wbt-erase.xml create mode 100644 gpsbabel/xmldoc/formats/wbt.xml diff --git a/gpsbabel/Makefile.in b/gpsbabel/Makefile.in index dc2cca00e..13829c549 100644 --- a/gpsbabel/Makefile.in +++ b/gpsbabel/Makefile.in @@ -44,7 +44,8 @@ FMTS=magproto.o gpx.o geo.o mapsend.o mapsource.o garmin_tables.o \ vcf.o overlay.o kml.o google.o lowranceusr.o an1.o tomtom.o \ tef_xml.o maggeo.o pathaway.o vitosmt.o gdb.o bcr.o coto.o \ ignrando.o stmwpp.o msroute.o cst.o nmn4.o mag_pdb.o compegps.o \ - yahoo.o unicsv.o wfff_xml.o garmin_txt.o axim_gpb.o gpssim.o + yahoo.o unicsv.o wfff_xml.o garmin_txt.o axim_gpb.o gpssim.o \ + wbt-200.o FILTERS=position.o duplicate.o arcdist.o polygon.o smplrout.o \ reverse_route.o sort.o stackfilter.o trackfilter.o discard.o \ diff --git a/gpsbabel/msvc/GPSBabel.dsp b/gpsbabel/msvc/GPSBabel.dsp index 43b25bd20..c1fcba872 100644 --- a/gpsbabel/msvc/GPSBabel.dsp +++ b/gpsbabel/msvc/GPSBabel.dsp @@ -315,6 +315,10 @@ SOURCE=..\brauniger_iq.c # End Source File # Begin Source File +SOURCE=..\wbt-200.c +# End Source File +# Begin Source File + SOURCE=..\cetus.c # End Source File # Begin Source File diff --git a/gpsbabel/msvc/GPSBabel.vcproj b/gpsbabel/msvc/GPSBabel.vcproj index 0b91ed518..e233aae5b 100644 --- a/gpsbabel/msvc/GPSBabel.vcproj +++ b/gpsbabel/msvc/GPSBabel.vcproj @@ -175,6 +175,9 @@ + + diff --git a/gpsbabel/msvc/build.bat b/gpsbabel/msvc/build.bat index 06d5d1fad..26c752aa6 100644 --- a/gpsbabel/msvc/build.bat +++ b/gpsbabel/msvc/build.bat @@ -2,7 +2,7 @@ setlocal set include=%include%;expat;..\coldsync;c:\tools\c set SOURCEJEEPS=..\jeeps\gpsapp.c ..\jeeps\gpscom.c ..\jeeps\gpsmath.c ..\jeeps\gpsmem.c ..\jeeps\gpsprot.c ..\jeeps\gpsread.c ..\jeeps\gpsrqst.c ..\jeeps\gpssend.c ..\jeeps\gpsserial.c ..\jeeps\gpsusbread.c ..\jeeps\gpsusbsend.c ..\jeeps\gpsusbstub.c ..\jeeps\gpsusbwin.c ..\jeeps\gpsutil.c set SOURCEMAG=..\maggeo.c ..\magnav.c ..\magproto.c -set SOURCE=..\xmltag.c ..\strptime.c ..\trackfilter.c ..\gdb.c ..\bcr.c ..\discard.c ..\formspec.c ..\an1.c ..\arcdist.c ..\brauniger_iq.c ..\cetus.c ..\coastexp.c ..\coldsync\pdb.c ..\copilot.c ..\csv_util.c ..\delgpl.c ..\duplicate.c ..\easygps.c ..\filter_vecs.c ..\garmin.c ..\garmin_tables.c ..\gcdb.c ..\geo.c ..\geoniche.c ..\glogbook.c ..\google.c ..\gpilots.c ..\gpspilot.c ..\gpx.c ..\grtcirc.c ..\hiketech.c ..\holux.c ..\hsa_ndv.c ..\html.c ..\igc.c ..\internal_styles.c ..\kml.c ..\lowranceusr.c ..\main.c ..\mapopolis.c ..\mapsend.c ..\mapsource.c ..\mkshort.c ..\navicache.c ..\netstumbler.c ..\nmea.c ..\overlay.c ..\ozi.c ..\palmdoc.c ..\pathaway.c ..\pcx.c ..\polygon.c ..\position.c ..\psitrex.c ..\psp.c ..\queue.c ..\quovadis.c ..\reverse_route.c ..\route.c ..\saroute.c ..\shape.c ..\shapelib\dbfopen.c ..\shapelib\shpopen.c ..\smplrout.c ..\sort.c ..\stackfilter.c ..\tef_xml.c ..\text.c ..\tiger.c ..\tmpro.c ..\tomtom.c ..\tpg.c ..\util.c ..\util_crc.c ..\uuid.c ..\vcf.c ..\vecs.c ..\vitosmt.c ..\vmem.c ..\waypt.c ..\xcsv.c ..\xmlgeneric.c ..\fatal.c ..\globals.c ..\cet_util.c ..\cet.c ..\nmn5.c ..\nmn4.c ..\cst.c ..\msroute.c ..\stmwpp.c ..\ignrando.c ..\tpo.c +set SOURCE=..\xmltag.c ..\strptime.c ..\trackfilter.c ..\gdb.c ..\bcr.c ..\discard.c ..\formspec.c ..\an1.c ..\arcdist.c ..\brauniger_iq.c ..\wbt-200.c ..\cetus.c ..\coastexp.c ..\coldsync\pdb.c ..\copilot.c ..\csv_util.c ..\delgpl.c ..\duplicate.c ..\easygps.c ..\filter_vecs.c ..\garmin.c ..\garmin_tables.c ..\gcdb.c ..\geo.c ..\geoniche.c ..\glogbook.c ..\google.c ..\gpilots.c ..\gpspilot.c ..\gpx.c ..\grtcirc.c ..\hiketech.c ..\holux.c ..\hsa_ndv.c ..\html.c ..\igc.c ..\internal_styles.c ..\kml.c ..\lowranceusr.c ..\main.c ..\mapopolis.c ..\mapsend.c ..\mapsource.c ..\mkshort.c ..\navicache.c ..\netstumbler.c ..\nmea.c ..\overlay.c ..\ozi.c ..\palmdoc.c ..\pathaway.c ..\pcx.c ..\polygon.c ..\position.c ..\psitrex.c ..\psp.c ..\queue.c ..\quovadis.c ..\reverse_route.c ..\route.c ..\saroute.c ..\shape.c ..\shapelib\dbfopen.c ..\shapelib\shpopen.c ..\smplrout.c ..\sort.c ..\stackfilter.c ..\tef_xml.c ..\text.c ..\tiger.c ..\tmpro.c ..\tomtom.c ..\tpg.c ..\util.c ..\util_crc.c ..\uuid.c ..\vcf.c ..\vecs.c ..\vitosmt.c ..\vmem.c ..\waypt.c ..\xcsv.c ..\xmlgeneric.c ..\fatal.c ..\globals.c ..\cet_util.c ..\cet.c ..\nmn5.c ..\nmn4.c ..\cst.c ..\msroute.c ..\stmwpp.c ..\ignrando.c ..\tpo.c cl /c ..\coldsync\util.c -Focoldsyncutil.obj cl /c ..\gpsutil.c -Fogpsutil2.obj cl /Fegpsbabel.exe %source% %sourcejeeps% %sourcemag% coldsyncutil.obj gpsutil2.obj -DVERSION=\"1\" -D__WIN32__ -DWIN32_LEAN_AND_MEAN -DNO_USB Expat\libexpat.lib diff --git a/gpsbabel/vecs.c b/gpsbabel/vecs.c index 02c4bb0ef..d7f2e0eb5 100644 --- a/gpsbabel/vecs.c +++ b/gpsbabel/vecs.c @@ -106,6 +106,7 @@ extern ff_vecs_t vitosmt_vecs; extern ff_vecs_t wfff_xml_vecs; extern ff_vecs_t xcsv_vecs; extern ff_vecs_t yahoo_vecs; +extern ff_vecs_t wbt_vecs; static vecs_t vec_list[] = { @@ -380,6 +381,12 @@ vecs_t vec_list[] = { "Brauniger IQ Series Barograph Download", NULL }, + { + &wbt_vecs, + "wbt", + "Wintec WBT-100/200 GPS Download", + NULL + }, { &hiketech_vecs, "hiketech", diff --git a/gpsbabel/wbt-200.c b/gpsbabel/wbt-200.c new file mode 100644 index 000000000..74a56bba4 --- /dev/null +++ b/gpsbabel/wbt-200.c @@ -0,0 +1,337 @@ +/* + * Serial download of track data from a Wintec WBT-200. + * + * Copyright (C) 2006 Andy Armstrong + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111 USA + */ + +#include "defs.h" +#include "jeeps/gpsserial.h" +#include "grtcirc.h" +#include + +/* + A conversation looks like this + + >> $PFST,FIRMWAREVERSION + << $PFST,FIRMWAREVERSION,WBT200,3,31,6090,R2*77 + >> $PFST,NORMAL + << $PFST,NORMAL,*02 + >> $PFST,READLOGGER + << $PFST,READLOGGER,*17 + << 0xFFFF, , 0xFFFF + << (length + 1) * 12 bytes of data + << ==== + >> $PFST,NORMAL + << $PFST,NORMAL,*02 +*/ + +static gpsdevh *fd; +static char *port; +static char *erase; + +#define MYNAME "WBT-100/200" +#define PRESTRKNAME "PRESALTTRK" +#define NL "\x0D\x0A" + +/* Number of lines to skip while waiting for an ACK from a command. I've seen + * conversations with up to 30 lines of cruft before the response so 50 isn't + * too crazy. + */ +#define RETRIES 50 + +static void db(int l, const char *msg, ...) { + va_list ap; + va_start(ap, msg); + if (global_opts.debug_level >= l) { + vprintf(msg, ap); + } + va_end(ap); +} + +/* Read a single character from the serial port. Kind of gross but we do + * it like this so we can use Jeeps (which doesn't have an equivalent + * function). Returns -1 if no char is available, -2 on error or the + * retrieved char. + */ +static int rd_char() { + if (GPS_Serial_Chars_Ready(fd)) { + unsigned char c; + if (GPS_Serial_Read(fd, &c, 1) != 1) { + return -2; + } else { + return c; + } + } else { + return -1; + } +} + +/* Blocking version of above. It would be nicer to use select on the fd + * rather than spinning in a loop here - but we're trying to stay platform + * independent. + */ +static int rd_char_b() { + int c = rd_char(); + while (c == -1) { + c = rd_char(); + } + + return c; +} + +/* Swallow any pending output from GPS */ + +static int rd_drain() { + int c = rd_char(); + while (c >= 0) { + c = rd_char(); + } + + return c == -1 ? 0 : c; +} + +/* Read a line (up to 0x0A). Carriage returns are filtered out. Always tries to + * read an entire line but discards any characters beyond len (because we're + * only ever interested in fairly short lines. + * + * Returns the number of characters read or -2 on error. The buffer will contain + * the (possibly truncated) string without any line terminator characters. The + * buffer will always be null terminated. + */ +static int rd_line(char *buf, int len) { + int c, pos = 0, nr = 0; + c = rd_char_b(); + while (c >= 0 && c != 0x0A) { + nr++; + if (c != 0x0D && pos < len-1) { + buf[pos++] = (unsigned char) c; + } + c = rd_char_b(); + } + + buf[pos] = '\0'; + + return c < 0 ? c : nr; +} + +static int wr_cmd(const char *cmd) { + return GPS_Serial_Write(fd, cmd, strlen(cmd)); +} + +static void rd_init(const char *fname) { + port = xstrdup(fname); + + db(1, "Opening port...\n"); + if (!GPS_Serial_On(port, &fd)) { + fatal(MYNAME ": Can't initialise port '%s'\n", port); + } +} + +static void rd_deinit(void) { + db(1, "Closing port...\n"); + if (!GPS_Serial_Off(fd)) { + fatal(MYNAME ": Can't shut down port '%s'\n", port); + } + + xfree(port); +} + +static int starts_with(const char *buf, const char *pat) { + return memcmp(buf, pat, strlen(pat)) == 0; +} + +/* Send a command then wait for a line starting with the command string + * to be returned. + */ + +static void do_cmd(const char *cmd, const char *expect, char *buf, int len) { + int rc, try; + + if (rd_drain() < 0) { + fatal(MYNAME ": Read error\n"); + } + + wr_cmd(cmd); + wr_cmd(NL); + + db(2, "Cmd: %s\n", cmd); + + /* We may need to skip a load of data to start with - the unit streams + * NMEA data all the time so it's highly likely that it'll be in the + * middle of an NMEA sentence when we start listening. + */ + for (try = 0; try < RETRIES; try++) { + if (rc = rd_line(buf, len), rc < 0) { + fatal(MYNAME ": Read error\n"); + } + if (starts_with(buf, expect)) { + db(2, "Got: %s\n", buf); + return; + } + db(2, "Skip %d: %s\n", try, buf); + } + + fatal(MYNAME ": Bad response from unit\n"); +} + +static void do_simple(const char *cmd, char *buf, int len) { + do_cmd(cmd, cmd, buf, len); +} + +static void hd(const void *d, int len) { + const unsigned char *dd = d; + while (len-- > 0) { + db(3, "%02x ", *dd++); + } + db(3, "\n"); +} + +static void rd_buf(void *buf, int len) { + char *bp = buf; + + while (len > 0) { + int rc = GPS_Serial_Read(fd, bp, len); + if (rc < 0) { + fatal(MYNAME ": Read error\n"); + } + len -= rc; + bp += rc; + } +} + +static void data_read(void) { + /* Awooga! Awooga! Statically allocated buffer danger! + * Actually, it's OK because rd_line can read arbitrarily + * long lines returning only the first N characters + */ + char line_buf[100]; + int count, d; + gbuint32 tim, ptim; + double lat, lon; + double plat, plon; /* previous point */ + struct tm t; + time_t rtim; + route_head *route_head = NULL; + waypoint *wpt = NULL; + + do_simple("$PFST,FIRMWAREVERSION", line_buf, sizeof(line_buf)); + do_simple("$PFST,NORMAL", line_buf, sizeof(line_buf)); + do_simple("$PFST,READLOGGER", line_buf, sizeof(line_buf)); + + /* Now we're into binary mode */ + rd_buf(line_buf, 6); /* six byte header */ + count = le_read16(line_buf + 2) + 1; + if (count == 0x10000) { + count = 0; + } + db(1, "Reading %d data\n", count); + for (d = 0; d < count; d++) { + rd_buf(line_buf, 12); /* twelve byte record */ + tim = le_read32(line_buf + 0); + + lat = (double) ((gbint32) le_read32(line_buf + 4)) / 10000000; + lon = (double) ((gbint32) le_read32(line_buf + 8)) / 10000000; + + t.tm_sec = ((tim >> 0) & 0x3F); + t.tm_min = ((tim >> 6) & 0x3F); + t.tm_hour = ((tim >> 12) & 0x1F); + t.tm_mday = ((tim >> 17) & 0x1F); + t.tm_mon = ((tim >> 22) & 0x0F) - 1; + t.tm_year = ((tim >> 26) & 0x3F) + 100; + + rtim = mkgmtime(&t); + + if (lat >= 100) { + /* Start new track */ + lat -= 100; + route_head = NULL; + } else { + wpt = waypt_new(); + + wpt->latitude = lat;; + wpt->longitude = lon; + wpt->altitude = 0.0; + wpt->creation_time = rtim; + wpt->centiseconds = 0; + + /* OK to reuse buffer now */ + sprintf(line_buf, "WP%04d", d + 1); + wpt->shortname = xstrdup(line_buf); + + wpt->speed = radtometers( + gcdist(RAD(plat), RAD(plon), + RAD(lat), RAD(lon))) / + (rtim - ptim); + wpt->course = DEG(heading(RAD(plat), RAD(plon), + RAD(lat), RAD(lon))); + wpt->pdop = 0; + wpt->fix = fix_unknown; + wpt->sat = 0; + + if (NULL == route_head) { + db(1, "New Track\n"); + route_head = route_head_alloc(); + track_add_head(route_head); + } + + track_add_wpt(route_head, wpt); + } + + ptim = rtim; + plat = lat; + plon = lon; + } + + /* Erase data? */ + + if (*erase != '0') { + int f; + db(1, "Erasing data\n"); + for (f = 27; f <= 31; f++) { + sprintf(line_buf, "$PFST,REMOVEFILE,%d", f); + do_cmd(line_buf, "$PFST,REMOVEFILE", line_buf, sizeof(line_buf)); + } + db(1, "Reclaiming free space\n"); + for (f = 0; f <= 3; f++) { + sprintf(line_buf, "$PFST,FFSRECLAIM,%d", f); + do_cmd(line_buf, "$PFST,FFSRECLAIM", line_buf, sizeof(line_buf)); + } + } + + do_simple("$PFST,NORMAL", line_buf, sizeof(line_buf)); + +} + +static arglist_t wbt_args[] = { + { "erase", &erase, "Erase device data after download", + "0", ARGTYPE_BOOL, ARG_NOMINMAX }, + ARG_TERMINATOR +}; + +ff_vecs_t wbt_vecs = { + ff_type_serial, + { ff_cap_none, ff_cap_read, ff_cap_none }, + rd_init, + NULL, + rd_deinit, + NULL, + data_read, + NULL, + NULL, + wbt_args, + CET_CHARSET_UTF8, 1 /* master process: don't convert anything | CET-REVIEW */ +}; diff --git a/gpsbabel/xmldoc/formats/options/wbt-erase.xml b/gpsbabel/xmldoc/formats/options/wbt-erase.xml new file mode 100644 index 000000000..71aaf2c54 --- /dev/null +++ b/gpsbabel/xmldoc/formats/options/wbt-erase.xml @@ -0,0 +1 @@ +This option erases the track log from the device after download. \ No newline at end of file diff --git a/gpsbabel/xmldoc/formats/wbt.xml b/gpsbabel/xmldoc/formats/wbt.xml new file mode 100644 index 000000000..215f0fea1 --- /dev/null +++ b/gpsbabel/xmldoc/formats/wbt.xml @@ -0,0 +1,9 @@ +Serial download protocol for the Wintec WBT-200 GPS data logger. Although untested it is expected that this will also support the WBT-100. + +Wintec WBT-200 + + + Command showing WBT-200 download and erase over Bluetooth on Mac OS X + gpsbabel -i wbt,erase -f /dev/cu.WBT200-SPPslave-1 -o gpx -F out.gpx + + -- 2.30.2